package ca.etsmtl.logti.log619.lab05;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import ca.etsmtl.logti.log619.lab05.filter.EmailFilter;
import ca.etsmtl.utilities.ConsoleUtilities;
import ca.etsmtl.utilities.JarClassLoader;

/**
 * Class used to dynamically load the EmailFilters 
 * @author Maxime Caumartin <maxime.caumartin.1@ens.etsmtl.ca>
 */
public class FilterFactory
{
	/**
	 * The directory that the filters will be loaded from.
	 */
	private static String filterPath = "/filter/";
	
	/**
	 * The HashMap containing the loaded filters.
	 */
	private static HashMap<String,EmailFilter> filters = null;
	
	public static String getFilterPath()
	{
		return filterPath;
	}
	
	public static void setFilterPath(String path)
	{
		filterPath = path;
	}
	
	public static EmailFilter[] getFilters()
	{
		if (filters == null)
			loadFilters();
		if (FilterFactory.filters.size() != 0)
		{
			EmailFilter[] filters = new EmailFilter[FilterFactory.filters.size()];
			Collection<EmailFilter> vals = FilterFactory.filters.values();
			return vals.toArray(filters);
		}
		return new EmailFilter[0];
	}
	
	public static EmailFilter getFilter(String filterName)
	{
		return filters.get(filterName);
	}
	
	/**
	 * Jar loading method from "http://floris.ouwendijk.nl/blog/index.php?blog=2&title=jar_introspection&more=1&c=1&tb=1&pb=1"
	 */
	private static void loadFilters()
	{
		filters = new HashMap<String, EmailFilter>(5);
		try
		{
			// Determine if we are loading from a jar or directly from the class
			URL u = Application.class.getProtectionDomain().getCodeSource().getLocation();
			if (u.toString().endsWith(".jar"))
				jarLoader(u);
			else
			{
				ConsoleUtilities.debug("Loading from classPath");
				File d = new File(
						u.getPath().substring(0,u.getPath().lastIndexOf("/")).concat("/")
						.concat(FilterFactory.class.getPackage().getName().replace(".","/")).concat("/").concat(filterPath));
				
				if (d.exists())
					for (String file : d.list())
						if (file.endsWith(".jar"))
							jarLoader(new URL("file","",file));
						else
							addFile(file);
			}
			
			
			// Load all classes and jars in the "./filter" directory, if it exists 
			File dir = new File(u.getPath() + filterPath);
			if (dir.exists())
				for (String file : dir.list())
					if (file.endsWith(".jar"))
						jarLoader(new URL("file", "", file));
					else
						addFile(file);

		}
		catch(IOException e)
		{
			System.err.println(e.getLocalizedMessage()); 
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Filters through all the files in the jar URL passed in parameter, finds classes that implements EmailFilter, that are in a @ref filterPath package 
	 * and creates an instance of that class. 
	 * @param u The URL defining the jar file
	 * @throws URISyntaxException If a problem exists with the URL passed as parameter. Should not happen, however.
	 */
	private static void jarLoader(URL u) throws URISyntaxException
	{
		ConsoleUtilities.debug("Loading from jar " + u.toString());
		// If we are in a jar file, check if there are filters inside!
		try{
		JarFile jf = new JarFile(new File(u.toURI()));
		
		Enumeration<JarEntry> e = jf.entries(); // List all files
		while (e.hasMoreElements()) {
			JarEntry entry = e.nextElement(); 
			if ( entry.getName().contains(filterPath) &&  entry.getName().contains(".class"))
				if (!entry.getName().contains("EmailFilter.class")) // The name of the interface. Explicit removal
					if (!entry.getName().contains("$")) // Explicit removal of anonymous or internal classes
						if (!filters.containsKey(entry.getName()))
						{
							EmailFilter filter = (EmailFilter) new JarClassLoader(u).invokeClass(
									entry.getName().replace('/','.').substring(0, entry.getName().lastIndexOf('.'))); // Create the JarClassLoader and load the specified class
							if ( filter == null) // Class isn't implementing EmailFilter, continue... 
								continue;
							if (!filter.enabled())
								continue;
							filters.put(filter.getFilterName(), filter );
							ConsoleUtilities.debugP(".",String.format("Loading class : %1$s",entry.getName().substring(0, entry.getName().indexOf(".class"))));
						}
		}
		jf.close();
		}
		catch (IOException e)
		{
			return ;
		}
	}
	
	/**
	 * Adds a Object to the EmailFilter HashMap<EmailFilter>.
	 * @param file The class file that will be added to the HashMap.
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws ClassNotFoundException
	 */
	private static void addFile(String file) throws InstantiationException, IllegalAccessException, ClassNotFoundException
	{
		if (!file.contains("EmailFilter.class")) // The name of the interface. Explicit removal
			if (!file.contains("$")) // Explicit removal of anonymous or internal classes
				if(!file.contains(".svn"))
				if (!filters.containsKey(file))
				{
					EmailFilter filter = (EmailFilter) FilterFactory.class.getClassLoader().loadClass(
							String.format("%1$s.filter.%2$s", FilterFactory.class.getPackage().getName(),file.substring(0, file.indexOf(".class")))).newInstance();
					if (filter == null) // Class isn't implementing EmailFilter, return...
						return;
					if (!filter.enabled())
						return;
					filters.put(filter.getFilterName(), filter );
					ConsoleUtilities.debugP(".",String.format("Loading class : %1$s",file.substring(0, file.indexOf(".class"))));
				}
	}
}